package com.sync.platform.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * FTP utility for file transfers
 */
public class FTPUtil {

    private static final Logger logger = LoggerFactory.getLogger(FTPUtil.class);

    /**
     * Connect to FTP server
     *
     * @param host Server host
     * @param port Server port
     * @param username Server username
     * @param password Server password
     * @return FTPClient instance
     * @throws IOException if connection fails
     */
    public static FTPClient connect(String host, int port, String username, String password) throws IOException {
        FTPClient ftpClient = new FTPClient();
        ftpClient.setControlEncoding("UTF-8");
        
        try {
            // Connect to server
            ftpClient.connect(host, port);
            
            // Check reply code
            int reply = ftpClient.getReplyCode();
            if (!FTPReply.isPositiveCompletion(reply)) {
                ftpClient.disconnect();
                logger.error("FTP server refused connection");
                throw new IOException("FTP server refused connection");
            }
            
            // Login
            if (!ftpClient.login(username, password)) {
                ftpClient.disconnect();
                logger.error("FTP login failed for user: {}", username);
                throw new IOException("FTP login failed");
            }
            
            // Use passive mode (better for firewalls)
            ftpClient.enterLocalPassiveMode();
            
            // Set file type to binary
            ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
            
            return ftpClient;
        } catch (IOException e) {
            logger.error("Error connecting to FTP server {}: {}", host, e.getMessage(), e);
            if (ftpClient.isConnected()) {
                try {
                    ftpClient.disconnect();
                } catch (IOException e2) {
                    // Ignore
                }
            }
            throw e;
        }
    }

    /**
     * Upload a file to the FTP server
     *
     * @param ftpClient FTPClient instance
     * @param localFilePath Local file path
     * @param remoteFilePath Remote file path
     * @return true if upload successful, false otherwise
     */
    public static boolean uploadFile(FTPClient ftpClient, String localFilePath, String remoteFilePath) {
        try {
            File localFile = new File(localFilePath);
            if (!localFile.exists()) {
                logger.error("Local file does not exist: {}", localFilePath);
                return false;
            }
            
            // Create remote directories if they don't exist
            String remoteDir = remoteFilePath.substring(0, remoteFilePath.lastIndexOf('/'));
            createRemoteDirectories(ftpClient, remoteDir);
            
            // Upload file
            try (InputStream inputStream = new FileInputStream(localFile)) {
                return ftpClient.storeFile(remoteFilePath, inputStream);
            }
        } catch (IOException e) {
            logger.error("Error uploading file to FTP server: {}", e.getMessage(), e);
            return false;
        }
    }

    /**
     * Download a file from the FTP server
     *
     * @param ftpClient FTPClient instance
     * @param remoteFilePath Remote file path
     * @param localFilePath Local file path
     * @return true if download successful, false otherwise
     */
    public static boolean downloadFile(FTPClient ftpClient, String remoteFilePath, String localFilePath) {
        try {
            File localFile = new File(localFilePath);
            
            // Create parent directories if they don't exist
            File parentDir = localFile.getParentFile();
            if (parentDir != null && !parentDir.exists()) {
                parentDir.mkdirs();
            }
            
            // Download file
            try (OutputStream outputStream = new FileOutputStream(localFile)) {
                return ftpClient.retrieveFile(remoteFilePath, outputStream);
            }
        } catch (IOException e) {
            logger.error("Error downloading file from FTP server: {}", e.getMessage(), e);
            return false;
        }
    }

    /**
     * List files in a remote directory
     *
     * @param ftpClient FTPClient instance
     * @param remotePath Remote directory path
     * @return Array of FTPFile objects
     */
    public static FTPFile[] listFiles(FTPClient ftpClient, String remotePath) {
        try {
            return ftpClient.listFiles(remotePath);
        } catch (IOException e) {
            logger.error("Error listing files on FTP server: {}", e.getMessage(), e);
            return new FTPFile[0];
        }
    }

    /**
     * Create remote directories recursively
     *
     * @param ftpClient FTPClient instance
     * @param remotePath Remote directory path
     * @return true if operation successful, false otherwise
     */
    public static boolean createRemoteDirectories(FTPClient ftpClient, String remotePath) {
        if (remotePath == null || remotePath.isEmpty() || remotePath.equals("/")) {
            return true;
        }
        
        try {
            // Try to change to directory (to check if it exists)
            if (ftpClient.changeWorkingDirectory(remotePath)) {
                return true;
            }
            
            // Directory doesn't exist, create parent directories first
            int slashIndex = remotePath.lastIndexOf('/');
            if (slashIndex > 0) {
                String parentPath = remotePath.substring(0, slashIndex);
                if (!createRemoteDirectories(ftpClient, parentPath)) {
                    return false;
                }
            }
            
            // Create this directory
            return ftpClient.makeDirectory(remotePath);
        } catch (IOException e) {
            logger.error("Error creating remote directory: {}", e.getMessage(), e);
            return false;
        }
    }

    /**
     * Synchronize directories between local and remote FTP server
     *
     * @param ftpClient FTPClient instance
     * @param localPath Local directory path
     * @param remotePath Remote directory path
     * @param filePattern Optional file pattern (comma-separated list of patterns)
     * @param deleteSourceFiles Whether to delete source files after successful transfer
     * @return List of synchronized files
     */
    public static List<String> syncDirectories(FTPClient ftpClient, String localPath, String remotePath, 
                                              String filePattern, boolean deleteSourceFiles) {
        List<String> syncedFiles = new ArrayList<>();
        File localDir = new File(localPath);
        
        if (!localDir.exists() || !localDir.isDirectory()) {
            logger.error("Local directory does not exist: {}", localPath);
            return syncedFiles;
        }
        
        try {
            // Create remote directory if it doesn't exist
            createRemoteDirectories(ftpClient, remotePath);
            
            // List local files
            File[] localFiles = localDir.listFiles();
            if (localFiles == null) {
                return syncedFiles;
            }
            
            // Process file patterns
            String[] patterns = null;
            if (filePattern != null && !filePattern.trim().isEmpty()) {
                patterns = filePattern.split(",");
                for (int i = 0; i < patterns.length; i++) {
                    patterns[i] = patterns[i].trim().replace("*", ".*");
                }
            }
            
            // Process each file
            for (File file : localFiles) {
                if (file.isFile()) {
                    String fileName = file.getName();
                    
                    // Check if file matches pattern
                    if (patterns != null) {
                        boolean matched = false;
                        for (String pattern : patterns) {
                            if (fileName.matches(pattern)) {
                                matched = true;
                                break;
                            }
                        }
                        if (!matched) {
                            continue;
                        }
                    }
                    
                    // Upload file
                    String remoteFilePath = remotePath + (remotePath.endsWith("/") ? "" : "/") + fileName;
                    if (uploadFile(ftpClient, file.getAbsolutePath(), remoteFilePath)) {
                        syncedFiles.add(fileName);
                        
                        // Delete source file if required
                        if (deleteSourceFiles) {
                            if (!file.delete()) {
                                logger.warn("Failed to delete source file: {}", file.getAbsolutePath());
                            }
                        }
                    }
                } else if (file.isDirectory()) {
                    // Recursively process subdirectories
                    String newLocalPath = localPath + File.separator + file.getName();
                    String newRemotePath = remotePath + (remotePath.endsWith("/") ? "" : "/") + file.getName();
                    
                    List<String> subDirSyncedFiles = syncDirectories(ftpClient, newLocalPath, newRemotePath, 
                                                                   filePattern, deleteSourceFiles);
                    syncedFiles.addAll(subDirSyncedFiles);
                }
            }
        } catch (Exception e) {
            logger.error("Error synchronizing directories: {}", e.getMessage(), e);
        }
        
        return syncedFiles;
    }

    /**
     * Close FTP connection
     *
     * @param ftpClient FTPClient instance
     */
    public static void disconnect(FTPClient ftpClient) {
        if (ftpClient != null && ftpClient.isConnected()) {
            try {
                ftpClient.logout();
                ftpClient.disconnect();
            } catch (IOException e) {
                logger.error("Error disconnecting from FTP server: {}", e.getMessage(), e);
            }
        }
    }
} 